module Cache #(
    parameter integer ADDR_WIDTH = 64,
    parameter integer DATA_WIDTH = 64,
    parameter integer BANK_NUM   = 4,
    parameter integer CAPACITY   = 1024
) (
    input                     clk,
    input                     rstn,
    input  [  ADDR_WIDTH-1:0] addr_cpu,
    input  [  DATA_WIDTH-1:0] wdata_cpu,
    input                     wen_cpu,
    input  [DATA_WIDTH/8-1:0] wmask_cpu,
    input                     ren_cpu,
    output [  DATA_WIDTH-1:0] rdata_cpu,
    output                    hit_cpu,

    Mem_ift.Master mem_ift
);

    logic                      ren_mem;
    logic                      wen_mem;
    logic [    ADDR_WIDTH-1:0] raddr_mem;
    logic [    ADDR_WIDTH-1:0] waddr_mem;
    logic [  DATA_WIDTH*2-1:0] wdata_mem;
    logic [DATA_WIDTH*2/8-1:0] wmask_mem;
    logic [  DATA_WIDTH*2-1:0] rdata_mem;
    logic                      wvalid_mem;
    logic                      rvalid_mem;

    assign mem_ift.Mw.waddr = waddr_mem;
    assign mem_ift.Mr.raddr = raddr_mem;
    assign mem_ift.Mw.wen   = wen_mem;
    assign mem_ift.Mr.ren   = ren_mem;
    assign mem_ift.Mw.wdata = wdata_mem;
    assign mem_ift.Mw.wmask = wmask_mem;
    assign rdata_mem        = mem_ift.Sr.rdata;
    assign rvalid_mem       = mem_ift.Sr.rvalid;
    assign wvalid_mem       = mem_ift.Sw.wvalid;

    localparam BYTE_NUM = DATA_WIDTH / 8;
    localparam LINE_NUM = CAPACITY / 2 / (BANK_NUM * BYTE_NUM);
    localparam GRANU_LEN = $clog2(BYTE_NUM);
    localparam GRANU_BEGIN = 0;
    localparam GRANU_END = GRANU_BEGIN + GRANU_LEN - 1;
    localparam OFFSET_LEN = $clog2(BANK_NUM);
    localparam OFFSET_BEGIN = GRANU_END + 1;
    localparam OFFSET_END = OFFSET_BEGIN + OFFSET_LEN - 1;
    localparam INDEX_LEN = $clog2(LINE_NUM);
    localparam INDEX_BEGIN = OFFSET_END + 1;
    localparam INDEX_END = INDEX_BEGIN + INDEX_LEN - 1;
    localparam TAG_BEGIN = INDEX_END + 1;
    localparam TAG_END = ADDR_WIDTH - 1;
    localparam TAG_LEN = ADDR_WIDTH - TAG_BEGIN;
    typedef logic [TAG_LEN-1:0] tag_t;
    typedef logic [INDEX_LEN-1:0] index_t;
    typedef logic [OFFSET_LEN-1:0] offset_t;

    logic [         ADDR_WIDTH-1:0] addr_wb;
    logic [BANK_NUM*DATA_WIDTH-1:0] data_wb;
    logic                           busy_wb;
    logic                           need_wb;

    logic [         ADDR_WIDTH-1:0] addr_cache;
    logic                           miss_cache;
    logic                           set_cache;
    logic                           busy_rd;
    logic [         ADDR_WIDTH-1:0] addr_rd;
    logic [       DATA_WIDTH*2-1:0] data_rd;
    logic                           wen_rd;
    logic                           set_rd;
    logic                           finish_rd;
                 
    CacheBank #(
        .ADDR_WIDTH(ADDR_WIDTH),
        .DATA_WIDTH(DATA_WIDTH),
        .BANK_NUM  (BANK_NUM),
        .CAPACITY  (CAPACITY)
    ) cache_bank (
        .clk      (clk),
        .rstn     (rstn),
        .addr_cpu (addr_cpu),
        .wdata_cpu(wdata_cpu),
        .wen_cpu  (wen_cpu),
        .wmask_cpu(wmask_cpu),
        .ren_cpu  (ren_cpu),
        .rdata_cpu(rdata_cpu),
        .hit_cpu  (hit_cpu),

        .addr_wb(addr_wb),
        .data_wb(data_wb),
        .busy_wb(busy_wb),
        .need_wb(need_wb),

        .addr_cache(addr_cache),// cache 告知 CMU 失配数据的地址
        .miss_cache(miss_cache),// cache 告知 CMU 发生了失配
        .set_cache (set_cache), // cache 告知 CMU 需要写入的 way 的编号

        .busy_rd  (busy_rd), // CMU 告诉 cache 自己是否忙碌
        .addr_rd  (addr_rd), // CMU 告诉 cache 自己从内存读入数据的地址
        .data_rd  (data_rd), // CMU 告诉 cache 自己从内存读入数据的值
        .wen_rd   (wen_rd),  // CMU 告诉 cache 自己要修改对应 cachline 的值
        .set_rd   (set_rd),  // CMU 告诉 cache 自己要修改的 cache way 的编号
        .finish_rd(finish_rd)// CMU 告诉 cache 自己完成了所有的读操作，一个 cacheline 载入完毕
    );

    logic [  ADDR_WIDTH-1:0] addr_mem;
    logic [DATA_WIDTH*2-1:0] data_mem;
    logic [  OFFSET_LEN-2:0] bank_index;
    logic                    finish_wb;
    // reg [  ADDR_WIDTH-1:0] addr_mem_reg;
    // reg [DATA_WIDTH*2-1:0] data_mem_reg;
    // reg [  OFFSET_LEN-2:0] bank_index_reg;
    // reg                    finish_wb_reg;
    CacheWriteBuffer #(
        .ADDR_WIDTH(ADDR_WIDTH),
        .DATA_WIDTH(DATA_WIDTH),
        .BANK_NUM  (BANK_NUM)
    ) cache_write_buffer (
        .clk       (clk),
        .rstn      (rstn),
        .addr_wb   (addr_wb),
        .data_wb   (data_wb),
        .busy_wb   (busy_wb),
        .need_wb   (need_wb),
        .miss_cache(miss_cache),

        .addr_mem  (addr_mem),
        .data_mem  (data_mem),
        .bank_index(bank_index),
        .finish_wb (finish_wb)
    );


localparam IDLE = 2'b00,
           READ = 2'b01,
           WRITE = 2'b10;


reg [1:0] state;
integer count;
always @(posedge clk or negedge rstn) begin
    if(~rstn) begin
        state <= IDLE;
        count <= 0;
    end
    else
    case (state)
        IDLE: begin
            wen_rd <= 0;
            finish_rd <= 1'b0;
            if (miss_cache) begin
                state <= READ;
                count <= 0;
                set_rd <= set_cache;
                raddr_mem <= addr_cache;  // 发送读内存请求的起始地址
                ren_mem <= 1'b1;          // 激活读使能信号
                busy_rd <= 1'b1;          // 激活CMU忙碌信号
            end
            else begin
                busy_rd <= 0;
                finish_wb <= 0;
                wdata_mem <= 0;
                waddr_mem <= 0;
                wen_mem <= 1'b0;  // 关闭写使能信号
            end
        end

        READ: begin
            if (rvalid_mem) begin
                addr_rd <= raddr_mem;
                wen_rd <= 1'b1;           // 发送写Cache请求的信号
                // set_rd <= set_cache;
                data_rd <= rdata_mem;
                count <= count + 1;
                case(count)
                    0: begin
                        raddr_mem <= raddr_mem + 16;
                    end
                    1: begin
                        finish_rd <= 1'b1;
                        ren_mem <= 1'b0;        // 关闭读使能信号
                        if(busy_wb) begin       // 如果有脏数据需要写进内存
                            state <= WRITE;     // 切换到 WRITE
                            count <= 0;
                            waddr_mem <= addr_mem;
                            wdata_mem <= data_mem;
                            wmask_mem <= 16'hFFFF;
                            bank_index <= 0;
                            wen_mem <= 1'b1;    // 开启写使能信号
                        end
                        else begin
                            state <= IDLE;
                            count <= 0;
                        end
                    end
                endcase
            end
        end
        WRITE: begin
            set_rd <= 0;
            wen_rd <= 0;
            finish_rd <= 0;
            busy_rd <= 0;
            wdata_mem <= data_mem;
            if(wvalid_mem) begin
                count <= count + 1;
                case(count)
                    0: begin
                        waddr_mem <= waddr_mem + 16;
                        bank_index <= bank_index + 1;
                    end
                    1: begin
                        finish_wb <= 1'b1;
                        count <= 0;
                        state <= IDLE;
                        bank_index <= 0;
                    end

                endcase
            end
        end
        default:begin
        end
    endcase
end

// always @(posedge clk or negedge rstn) begin
//     if (!rstn) begin
//         state <= IDLE;
//     end else begin
//         state <= next_state;
//     end
// end


// always @(*) begin
//     case (state)
//         IDLE: begin
//         end
//         READ: begin
            
//             while(mem_ift.Sr.rvalid==0) begin

//                 count++;
//             end
//         end
//         WRITE: begin
//         end
//         default: begin
//         end
//     endcase
// end

endmodule
